"""
飞书服务层
处理飞书API调用和业务逻辑
"""

import requests
import json
import time
import hashlib
import hmac
import base64
from typing import Dict, Optional, Any
from src.config import feishu_settings
from src.feishu.models import FeishuOAuthToken, FeishuUserInfo
import asyncio
import random


class FeishuService:
    """飞书服务类"""
    
    def __init__(self):
        self.base_url = feishu_settings.API_BASE_URL
        self.app_id = feishu_settings.APP_ID
        self.app_secret = feishu_settings.APP_SECRET
        self._app_access_token = None
        self._app_token_expires_at = 0
    
    def get_app_access_token(self) -> str:
        """获取应用访问令牌"""
        if self._app_access_token and time.time() < self._app_token_expires_at:
            return self._app_access_token
        
        url = f"{self.base_url}/auth/v3/app_access_token/internal"
        payload = {
            "app_id": self.app_id,
            "app_secret": self.app_secret
        }
        
        # 添加重试机制和超时控制
        for attempt in range(3):
            try:
                response = requests.post(url, json=payload, timeout=15)  # 增加超时时间到15秒
                response.raise_for_status()
                data = response.json()
                
                if data.get('code') == 0:
                    self._app_access_token = data['app_access_token']
                    self._app_token_expires_at = time.time() + data['expire'] - 60  # 提前60秒过期
                    return self._app_access_token
                else:
                    raise Exception(f"获取应用访问令牌失败: {data.get('msg')}")
            except Exception as e:
                if attempt == 2:  # 最后一次尝试
                    raise Exception(f"获取应用访问令牌错误: {str(e)}")
                # 指数退避
                time.sleep(2 ** attempt + random.uniform(0, 1))
        
        # 如果所有重试都失败，抛出异常
        raise Exception("获取应用访问令牌失败")
    
    def get_tenant_access_token(self) -> str:
        """获取租户访问令牌"""
        url = f"{self.base_url}/auth/v3/tenant_access_token/internal"
        payload = {
            "app_id": self.app_id,
            "app_secret": self.app_secret
        }
        
        # 添加重试机制和超时控制
        for attempt in range(3):
            try:
                response = requests.post(url, json=payload, timeout=15)  # 增加超时时间到15秒
                response.raise_for_status()
                data = response.json()
                
                if data.get('code') == 0:
                    return data['tenant_access_token']
                else:
                    raise Exception(f"获取租户访问令牌失败: {data.get('msg')}")
            except Exception as e:
                if attempt == 2:  # 最后一次尝试
                    raise Exception(f"获取租户访问令牌错误: {str(e)}")
                # 指数退避
                time.sleep(2 ** attempt + random.uniform(0, 1))
        
        # 如果所有重试都失败，抛出异常而不是返回None
        raise Exception("获取租户访问令牌失败")
    
    def send_message(self, receive_id: str, msg_type: str, content: Dict[str, Any]) -> Dict[str, Any]:
        """发送消息"""
        try:
            token = self.get_tenant_access_token()
        except Exception as e:
            # 如果获取令牌失败，返回错误但不抛出异常
            return {"error": str(e)}
        
        url = f"{self.base_url}/im/v1/messages"
        
        headers = {
            "Authorization": f"Bearer {token}",
            "Content-Type": "application/json"
        }
        
        # 根据飞书文档，需要添加额外的查询参数
        params = {
            "receive_id_type": "chat_id"  # 默认使用chat_id作为接收者类型
        }
        
        payload = {
            "receive_id": receive_id,
            "msg_type": msg_type,
            "content": json.dumps(content)
        }
        
        # 添加重试机制和超时控制
        for attempt in range(3):
            try:
                response = requests.post(url, headers=headers, params=params, json=payload, timeout=15)  # 增加超时时间到15秒
                response.raise_for_status()
                return response.json()
            except Exception as e:
                if attempt == 2:  # 最后一次尝试
                    return {"error": f"发送消息失败: {str(e)}"}
                # 指数退避
                time.sleep(2 ** attempt + random.uniform(0, 1))
        
        # 如果所有重试都失败，确保有返回值
        return {"error": "发送消息失败"}
    
    def send_text_message(self, receive_id: str, text: str, receive_id_type: str = "chat_id") -> Dict[str, Any]:
        """发送文本消息"""
        content = {
            "text": text
        }
        
        # 添加重试机制和超时控制
        for attempt in range(3):
            try:
                result = self.send_message(receive_id, "text", content)
                if "error" not in result:
                    return result
                elif attempt == 2:  # 最后一次尝试
                    return result
                # 指数退避
                time.sleep(2 ** attempt + random.uniform(0, 1))
            except Exception as e:
                if attempt == 2:  # 最后一次尝试
                    return {"error": str(e)}
                # 指数退避
                time.sleep(2 ** attempt + random.uniform(0, 1))
        
        # 确保有返回值
        return {"error": "发送文本消息失败"}
    
    async def async_send_text_message(self, receive_id: str, text: str, receive_id_type: str = "chat_id") -> Dict[str, Any]:
        """异步发送文本消息"""
        # 使用线程池执行同步方法
        loop = asyncio.get_event_loop()
        return await loop.run_in_executor(None, self.send_text_message, receive_id, text, receive_id_type)
    
    def get_user_info(self, user_id: str) -> Optional[FeishuUserInfo]:
        """获取用户信息"""
        try:
            token = self.get_tenant_access_token()
        except Exception as e:
            raise Exception(f"获取租户访问令牌失败: {str(e)}")
        
        url = f"{self.base_url}/contact/v3/users/{user_id}"
        headers = {
            "Authorization": f"Bearer {token}"
        }
        params = {
            "user_id_type": "user_id"
        }
        
        try:
            response = requests.get(url, headers=headers, params=params, timeout=10)
            response.raise_for_status()
            data = response.json()
            
            if data.get('code') == 0:
                user_data = data['data']['user']
                return FeishuUserInfo(
                    user_id=user_data['user_id'],
                    union_id=user_data.get('union_id', ''),
                    open_id=user_data.get('open_id', ''),
                    name=user_data.get('name', ''),
                    en_name=user_data.get('en_name', ''),
                    email=user_data.get('email', ''),
                    mobile=user_data.get('mobile', ''),
                    avatar_url=user_data.get('avatar_url', '')
                )
            else:
                raise Exception(f"获取用户信息失败: {data.get('msg')}")
        except Exception as e:
            raise Exception(f"获取用户信息错误: {str(e)}")
    
    def verify_signature(self, timestamp: str, nonce: str, signature: str, body: str) -> bool:
        """验证飞书请求签名"""
        if not feishu_settings.VERIFICATION_TOKEN:
            return False
        
        # 拼接签名基础字符串
        sign_str = f"{timestamp}\n{nonce}\n{body}\n"
        
        # 计算签名
        sign = base64.b64encode(
            hmac.new(
                feishu_settings.VERIFICATION_TOKEN.encode('utf-8'),
                sign_str.encode('utf-8'),
                digestmod=hashlib.sha256
            ).digest()
        ).decode('utf-8')
        
        return sign == signature
    
    def handle_oauth_callback(self, code: str) -> FeishuOAuthToken:
        """处理OAuth回调，获取用户访问令牌"""
        url = f"{self.base_url}/authen/v1/oidc/access_token"
        
        payload = {
            "grant_type": "authorization_code",
            "code": code,
            "client_id": self.app_id,
            "client_secret": self.app_secret
        }
        
        try:
            response = requests.post(url, json=payload, timeout=10)
            response.raise_for_status()
            data = response.json()
            
            if data.get('code') == 0:
                return FeishuOAuthToken.from_dict(data['data'])
            else:
                raise Exception(f"OAuth回调处理失败: {data.get('msg')}")
        except Exception as e:
            raise Exception(f"OAuth回调处理错误: {str(e)}")
